// JSON Web Token (JWT) 中文封装
const jwt = require('jsonwebtoken');

/**
 * 生成JWT令牌
 * @param {Object} 载荷 - 要编码的数据
 * @param {string} 密钥 - 签名密钥
 * @param {Object} 选项 - 编码选项（过期时间、算法等）
 * @returns {string} 生成的JWT令牌
 */
function 生成令牌(载荷, 密钥, 选项) {
  if (!选项) {
    选项 = {};
  }
  return jwt.sign(载荷, 密钥, 选项);
}

/**
 * 验证JWT令牌
 * @param {string} 令牌 - 要验证的JWT令牌
 * @param {string|Object} 密钥 - 验证密钥或密钥查找函数
 * @param {Object} 选项 - 验证选项
 * @returns {Object} 解码后的令牌数据
 * @throws {Error} 验证失败时抛出错误
 */
function 验证令牌(令牌, 密钥, 选项) {
  if (!选项) {
    选项 = {};
  }
  return jwt.verify(令牌, 密钥, 选项);
}

/**
 * 解码JWT令牌（不验证签名）
 * @param {string} 令牌 - 要解码的JWT令牌
 * @param {Object} 选项 - 解码选项
 * @returns {Object} 解码后的令牌数据
 */
function 解码令牌(令牌, 选项) {
  if (!选项) {
    选项 = {};
  }
  return jwt.decode(令牌, 选项);
}

/**
 * 为Express生成JWT中间件
 * @param {string|Object} 密钥 - 验证密钥或配置对象
 * @param {Object} 选项 - 中间件选项
 * @returns {Function} Express中间件函数
 */
function 创建认证中间件(密钥, 选项) {
  return function(req, res, next) {
    // 从请求头获取令牌
    const 令牌 = req.headers.authorization?.split(' ')[1];
    
    if (!令牌) {
      return res.status(401).json({ 错误: '未提供认证令牌' });
    }
    
    try {
      // 验证令牌
      const 解码数据 = 验证令牌(令牌, 密钥, 选项);
      
      // 将解码后的数据添加到请求对象
      req.user = 解码数据;
      next();
    } catch (错误) {
      if (错误.name === 'TokenExpiredError') {
        return res.status(401).json({ 错误: '令牌已过期' });
      } else if (错误.name === 'JsonWebTokenError') {
        return res.status(401).json({ 错误: '无效的令牌' });
      }
      return res.status(401).json({ 错误: '认证失败' });
    }
  };
}

/**
 * 生成刷新令牌
 * @param {Object} 用户数据 - 用户标识信息
 * @param {string} 刷新密钥 - 刷新令牌专用密钥
 * @param {number} 有效期 - 刷新令牌有效期（秒）
 * @returns {string} 刷新令牌
 */
function 生成刷新令牌(用户数据, 刷新密钥, 有效期 = 2592000) { // 默认30天
  return 生成令牌({
    id: 用户数据.id || 用户数据.user_id,
    type: 'refresh'
  }, 刷新密钥, {
    expiresIn: 有效期,
    algorithm: 'HS256'
  });
}

/**
 * 使用刷新令牌生成新的访问令牌
 * @param {string} 刷新令牌 - 有效的刷新令牌
 * @param {string} 刷新密钥 - 刷新令牌专用密钥
 * @param {string} 访问密钥 - 访问令牌专用密钥
 * @param {Object} 附加载荷 - 要添加到新访问令牌的额外数据
 * @param {number} 访问有效期 - 访问令牌有效期（秒）
 * @returns {Object} 包含新访问令牌和刷新令牌的对象
 * @throws {Error} 验证失败时抛出错误
 */
async function 刷新访问令牌(刷新令牌, 刷新密钥, 访问密钥, 附加载荷 = {}, 访问有效期 = 3600) { // 默认1小时
  try {
    // 验证刷新令牌
    const 解码数据 = 验证令牌(刷新令牌, 刷新密钥);
    
    // 确保这是刷新令牌
    if (解码数据.type !== 'refresh') {
      throw new Error('无效的刷新令牌类型');
    }
    
    // 合并用户ID和附加数据
    const 载荷 = {
      id: 解码数据.id,
      ...附加载荷
    };
    
    // 生成新的访问令牌
    const 新访问令牌 = 生成令牌(载荷, 访问密钥, {
      expiresIn: 访问有效期,
      algorithm: 'HS256'
    });
    
    // 可选：生成新的刷新令牌
    const 新刷新令牌 = 生成刷新令牌({ id: 解码数据.id }, 刷新密钥);
    
    return {
      访问令牌: 新访问令牌,
      刷新令牌: 新刷新令牌,
      过期时间: 访问有效期
    };
  } catch (错误) {
    throw new Error('令牌刷新失败: ' + 错误.message);
  }
}

/**
 * 设置令牌过期时间
 * @param {number|string} 时间 - 过期时间（秒或描述性字符串如'1h', '2d'）
 * @returns {Object} 包含expiresIn属性的选项对象
 */
function 设置过期时间(时间) {
  return { expiresIn: 时间 };
}

/**
 * 创建JWT错误处理函数
 * @param {Function} 自定义处理 - 可选的自定义错误处理函数
 * @returns {Function} 错误处理函数
 */
function 创建错误处理器(自定义处理) {
  return function(错误, req, res, next) {
    if (custom处理 && typeof custom处理 === 'function') {
      return custom处理(错误, req, res, next);
    }
    
    if (错误.name === 'TokenExpiredError') {
      return res.status(401).json({ 错误: '认证令牌已过期', 过期时间: 错误.expiredAt });
    } else if (错误.name === 'JsonWebTokenError') {
      return res.status(401).json({ 错误: '无效的认证令牌' });
    } else if (错误.name === 'NotBeforeError') {
      return res.status(401).json({ 错误: '令牌尚未生效' });
    }
    
    return res.status(500).json({ 错误: '内部服务器错误' });
  };
}

/**
 * 生成具有不同级别的令牌（管理员、用户等）
 * @param {Object} 用户数据 - 用户信息
 * @param {string} 密钥 - 签名密钥
 * @param {string} 角色 - 用户角色
 * @param {number} 有效期 - 令牌有效期（秒）
 * @returns {string} JWT令牌
 */
function 生成角色令牌(用户数据, 密钥, 角色, 有效期 = 3600) {
  const 载荷 = {
    id: 用户数据.id,
    用户名: 用户数据.用户名,
    角色: 角色,
    ...用户数据
  };
  
  return 生成令牌(载荷, 密钥, 设置过期时间(有效期));
}

/**
 * 验证角色令牌，检查用户角色
 * @param {string} 令牌 - JWT令牌
 * @param {string} 密钥 - 验证密钥
 * @param {Array<string>} 允许的角色 - 允许的角色列表
 * @returns {Object} 解码后的令牌数据
 * @throws {Error} 验证失败或角色不匹配时抛出错误
 */
function 验证角色令牌(令牌, 密钥, 允许的角色) {
  const 解码数据 = 验证令牌(令牌, 密钥);
  
  if (!允许的角色.includes(解码数据.角色)) {
    throw new Error('权限不足，需要以下角色之一: ' + 允许的角色.join(', '));
  }
  
  return 解码数据;
}

/**
 * 为Express创建角色验证中间件
 * @param {Array<string>} 允许的角色 - 允许的角色列表
 * @returns {Function} Express中间件函数
 */
function 创建角色验证中间件(允许的角色) {
  return function(req, res, next) {
    if (!req.user) {
      return res.status(401).json({ 错误: '未认证的用户' });
    }
    
    if (!允许的角色.includes(req.user.角色)) {
      return res.status(403).json({ 错误: '权限不足' });
    }
    
    next();
  };
}

/**
 * 加密令牌中的敏感数据
 * @param {Object} 数据 - 要加密的数据
 * @param {string} 加密密钥 - 用于加密的密钥
 * @returns {string} 加密后的数据（Base64编码）
 */
function 加密敏感数据(数据, 加密密钥) {
  const crypto = require('crypto');
  const 算法 = 'aes-256-cbc';
  const iv = crypto.randomBytes(16);
  const 加密器 = crypto.createCipheriv(算法, Buffer.from(加密密钥.padEnd(32, '0').slice(0, 32)), iv);
  
  let 加密数据 = 加密器.update(JSON.stringify(数据), 'utf8', 'base64');
  加密数据 += 加密器.final('base64');
  
  return {
    iv: iv.toString('base64'),
    数据: 加密数据
  };
}

/**
 * 解密令牌中的敏感数据
 * @param {Object} 加密数据 - 包含iv和数据的对象
 * @param {string} 加密密钥 - 用于解密的密钥
 * @returns {Object} 解密后的数据
 */
function 解密敏感数据(加密数据, 加密密钥) {
  const crypto = require('crypto');
  const 算法 = 'aes-256-cbc';
  const 解密器 = crypto.createDecipheriv(
    算法,
    Buffer.from(加密密钥.padEnd(32, '0').slice(0, 32)),
    Buffer.from(加密数据.iv, 'base64')
  );
  
  let 解密数据 = 解密器.update(加密数据.数据, 'base64', 'utf8');
  解密数据 += 解密器.final('utf8');
  
  return JSON.parse(解密数据);
}

/**
 * 从请求中提取令牌
 * @param {Object} 请求对象 - Express请求对象
 * @param {string} 位置 - 令牌位置（'header', 'cookie', 'body'）
 * @param {string} 名称 - 令牌名称或键
 * @returns {string|null} 提取的令牌或null
 */
function 从请求提取令牌(请求对象, 位置 = 'header', 名称 = 'authorization') {
  switch (位置.toLowerCase()) {
    case 'header':
      const 头信息 = 请求对象.headers[名称.toLowerCase()];
      return 头信息 ? 头信息.replace('Bearer ', '') : null;
      
    case 'cookie':
      return 请求对象.cookies && 请求对象.cookies[名称] ? 请求对象.cookies[名称] : null;
      
    case 'body':
      return 请求对象.body && 请求对象.body[名称] ? 请求对象.body[名称] : null;
      
    default:
      return null;
  }
}

/**
 * 生成JWT配置对象
 * @param {string} 访问密钥 - 访问令牌密钥
 * @param {string} 刷新密钥 - 刷新令牌密钥
 * @param {number} 访问过期时间 - 访问令牌过期时间（秒）
 * @param {number} 刷新过期时间 - 刷新令牌过期时间（秒）
 * @returns {Object} JWT配置对象
 */
function 创建配置(访问密钥, 刷新密钥, 访问过期时间 = 3600, 刷新过期时间 = 2592000) {
  return {
    访问密钥,
    刷新密钥,
    访问过期时间,
    刷新过期时间,
    算法: 'HS256'
  };
}

module.exports = {
  // 核心功能
  生成令牌,
  验证令牌,
  解码令牌,
  
  // Express中间件
  创建认证中间件,
  创建错误处理器,
  创建角色验证中间件,
  
  // 令牌管理
  生成刷新令牌,
  刷新访问令牌,
  设置过期时间,
  生成角色令牌,
  验证角色令牌,
  
  // 工具函数
  加密敏感数据,
  解密敏感数据,
  从请求提取令牌,
  创建配置,
  
  // 获取原始模块
  获取原始JWT: () => jwt,
  
  // 版本信息
  版本: '1.0.0',
  JWT版本: require('jsonwebtoken/package.json').version
};

// 支持的算法常量
module.exports.算法 = {
  HS256: 'HS256',
  HS384: 'HS384',
  HS512: 'HS512',
  RS256: 'RS256',
  RS384: 'RS384',
  RS512: 'RS512',
  ES256: 'ES256',
  ES384: 'ES384',
  ES512: 'ES512',
  PS256: 'PS256',
  PS384: 'PS384',
  PS512: 'PS512',
  none: 'none'
};

// 过期时间常量（秒）
module.exports.过期时间 = {
  一小时: 3600,
  一天: 86400,
  一周: 604800,
  两周: 1209600,
  一个月: 2592000,
  三个月: 7776000,
  六个月: 15552000,
  一年: 31536000
};